﻿using NAudio.Vorbis;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InteractiveMusicPlayerMobile.辅助类
{
    public class 永续控制
    {
        Dictionary<int, VorbisWaveReader> OGG读取器组 = new Dictionary<int, VorbisWaveReader>();
        public 永续式 永续流;
        /// <summary>
        /// 储存永续模式播放信息的类
        /// </summary>
        public class 信息
        {
            public int 缓冲区大小 { get; set; }
            public double 当前播放bit_实时 { get; set; }
            public double 当前播放时间_实时 { get; set; }
            public double 每拍毫秒 { get; set; }
            public int 每拍毫秒_去除小数 { get; set; }
            public double 缓冲区时间_毫秒 { get; set; }
            public double 每毫秒bit { get; set; }
            public double 每拍Bit { get; set; }
            public int 当前拍 { get; set; }
            public int 当前小节 { get; set; }
            public int 当前拍数 { get; set; }
            public double 下一拍的字节 { get; set; }
            public TimeSpan 当前时长 { get; set; }
            public TimeSpan 持续时长 { get; set; }
            public int 循环次数 { get; set; }
        }
        object 线程锁 = new object();

        /// <summary>
        /// 启动一个永续模式播放的音乐实例
        /// </summary>
        /// <param name="播放列表">用列表储存的一个或多个文件名</param>
        /// <param name="LOOP模式">是否使用Loop永续循环</param>
        public 永续控制(List<string> 播放列表, bool LOOP模式 = true)
        {
            //初始化流
            foreach (string 文件路径 in 播放列表)
            {
                初始化音乐(播放列表.IndexOf(文件路径), 文件路径, LOOP模式);
            }
            //建立播放器并开始播放
            永续流 = new 永续式(OGG读取器组);
            永续式.运行状态.Loop模式 = LOOP模式;
        }
        /// <summary>
        /// 初始化OGG音乐
        /// </summary>
        /// <param name="加入下标">输入要加入的音乐ID，将作为该音乐播放的唯一识别号</param>
        /// <param name="文件路径">音乐的完整文件路径</param>
        /// <param name="LOOP模式">是否使用Loop循环播放，如果选择不使用，将在循环最后一个文件后结束</param>
        private void 初始化音乐(int 加入下标, string 文件路径, bool LOOP模式 = true)
        {
            lock (线程锁)
            {
                NAudio.Vorbis.VorbisWaveReader OGG音频 = new NAudio.Vorbis.VorbisWaveReader(文件路径);
                OGG读取器组.Add(加入下标, OGG音频);
            }

        }

      

    }
    public class 永续式 : IWaveProvider
    {
        #region 状态引用
        public Dictionary<int, VorbisWaveReader> OGG读取器组;
       
        private static float 当前音量 = 1;
        public 运行状态.淡入淡出状态 淡入淡出设定 { get; set; } = 运行状态.淡入淡出状态.不启动;
        public long 播放字节位置 { get; set; } = 0;
        public long 总字节位置 { get; set; } = 0;
        public TimeSpan 当前时长 { get; set; }
        public TimeSpan 总时长 { get; set; }
        public int 循环次数 { get; set; }
        public int 当前播放章节 { get; set; }
        private static bool 正在淡出 = false;
        private static bool 已淡出 = false;
        public float 已淡入淡出段数 = 1;
        public bool 设定静音 = false;

        public class 运行状态
        {

            public static bool Loop模式 = true;
            public static bool 自动淡入淡出 = false;
            public static float 淡出段数 = 10;
            public static 播放状态 状态 = 播放状态.播放;
            public enum 淡入淡出状态
            {
                启动淡出,
                启动淡入,
                不启动
            }
            public enum 播放状态
            {
                暂停,
                播放,
                停止
            }
        }
        #endregion
        #region 构造与析构函数
        /// <summary>
        /// 外部调用请新运行永续控制类
        /// </summary>
        /// <param name="传入OGG读取器组"></param>
        /// <param name="传入播放器"></param>
        /// <param name="控制类"></param>
        public 永续式(Dictionary<int, VorbisWaveReader> 传入OGG读取器组)
        {
            OGG读取器组 = 传入OGG读取器组;
            当前播放章节 = 0;
            WaveFormat = 传入OGG读取器组[当前播放章节].WaveFormat;

        }
        ~永续式()
        {
            Console.WriteLine("播放类已析构");
        }
        #endregion
        #region 接口必要函数
        /// <summary>
        ///  接口必须实现的函数，当播放器init后，NAudio将自动调用此方法来获取波形数据，当读入0时将停止
        ///  该接口中需要对一个读取器进行递归调用Read获取数据
        /// </summary>
        /// <param name="缓冲区">NAudio内部的缓冲区</param>
        /// <param name="起始位置">NAudio中设定音频播放的起始位置，需要输入字节数</param>
        /// <param name="读取大小">指示要将  缓冲区  从  起始位置  读取多大的音频，最大  不可超过  缓冲区  的  最大长度</param>
        /// <returns>返回此次读取的波形大小</returns>
        public int Read(byte[] 缓冲区, int 起始位置, int 读取大小)
        {

            int 实际读取字节 = 0; //实际读取字节数
                            //循环逻辑，若文件剩余大小已不足以完成本次读取，则先更换流，然后把读取器的位置设为0
            if (OGG读取器组[当前播放章节].Length - OGG读取器组[当前播放章节].Position <= 读取大小 - 实际读取字节)
            {
                //如果关闭了Loop模式，则在最后一个文件播放完成后结束播放
                if (运行状态.Loop模式 == false && 当前播放章节 == OGG读取器组.Count - 1)
                {
                    实际读取字节 += OGG读取器组[当前播放章节].Read(缓冲区, 起始位置 + 实际读取字节, (int)(OGG读取器组[当前播放章节].Length - OGG读取器组[当前播放章节].Position));
                    return 0;
                }
                实际读取字节 += OGG读取器组[当前播放章节].Read(缓冲区, 起始位置 + 实际读取字节, (int)(OGG读取器组[当前播放章节].Length - OGG读取器组[当前播放章节].Position));
                更换流();
                OGG读取器组[当前播放章节].Position = 0;

            }
            //如果刚刚更换了流，这里会把下一章节的数据补充进缓冲区的剩余部分，避免造成空数据
            实际读取字节 += OGG读取器组[当前播放章节].Read(缓冲区, 起始位置 + 实际读取字节, 读取大小 - 实际读取字节);
            特效应用(缓冲区, 起始位置, 读取大小);
           
            return 实际读取字节; //无论如何都要返回已经读取了这么多字节

        }


        /// <summary>
        /// 接口必须实现的函数，获取当前播放流的类型
        /// 修改此处或许决定播放的文件
        /// 请设置为其读取器的.WaveFormat
        /// </summary>
        public WaveFormat WaveFormat { get; private set; }
        #endregion
        #region 外部交互逻辑
        public void 特效应用(byte[] 缓冲区, int 起始位置, int 读取大小)
        {


        }
        #endregion
        #region 特效函数
        public void 静音(byte[] 缓冲流, int 流开始点, int 流结束点)
        {

            unsafe
            {
                正在淡出 = true;
                float 左声道A, 右声道A, 左声道B, 右声道B;
                fixed (byte* 缓冲区指针 = 缓冲流)
                {
                    fixed (byte* 临时缓冲区指针 = 缓冲流)
                    {
                        float* 缓冲区数据A = (float*)(缓冲区指针 + 流开始点);
                        float* 缓冲区数据B = (float*)临时缓冲区指针;
                        int 流终点 = 流结束点 / WaveFormat.BlockAlign * 2;
                        for (int i = 0; i < 流终点; i += 2)
                        {
                            float 音量倍率 = 0;

                            if (音量倍率 <= 0.01)
                            {

                                音量倍率 = 0.0f;
                                已淡出 = true;

                            }
                            当前音量 = 音量倍率;
                            左声道A = 缓冲区数据A[i] * 音量倍率;
                            右声道A = 缓冲区数据A[i + 1] * 音量倍率;
                            左声道B = 缓冲区数据B[i] * 音量倍率;
                            右声道B = 缓冲区数据B[i + 1] * 音量倍率;


                            缓冲区数据A[i] = 左声道A;
                            缓冲区数据B[i + 1] = 右声道B;


                        }
                        已淡入淡出段数 += 1;
                    }
                }

            }


        }
        public void 淡出流(byte[] 缓冲流, int 流开始点, int 流结束点)
        {

            unsafe
            {
                正在淡出 = true;
                float 左声道A, 右声道A, 左声道B, 右声道B;
                fixed (byte* 缓冲区指针 = 缓冲流)
                {
                    fixed (byte* 临时缓冲区指针 = 缓冲流)
                    {
                        float* 缓冲区数据A = (float*)(缓冲区指针 + 流开始点);
                        float* 缓冲区数据B = (float*)临时缓冲区指针;
                        int 流终点 = 流结束点 / WaveFormat.BlockAlign * 2;
                        for (int i = 0; i < 流终点; i += 2)
                        {
                            float 音量倍率 = (float)1 - (float)已淡入淡出段数 / 运行状态.淡出段数;

                            if (音量倍率 <= 0.01)
                            {

                                音量倍率 = 0.0f;
                                已淡出 = true;

                            }
                            当前音量 = 音量倍率;
                            左声道A = 缓冲区数据A[i] * 音量倍率;
                            右声道A = 缓冲区数据A[i + 1] * 音量倍率;
                            左声道B = 缓冲区数据B[i] * 音量倍率;
                            右声道B = 缓冲区数据B[i + 1] * 音量倍率;


                            缓冲区数据A[i] = 左声道A;
                            缓冲区数据B[i + 1] = 右声道B;


                        }
                        已淡入淡出段数 += 1;
                    }
                }

            }


        }
        public void 淡入流(byte[] 缓冲流, int 流开始点, int 流结束点)
        {

            unsafe
            {
                正在淡出 = true;
                float 左声道A, 右声道A, 左声道B, 右声道B;
                fixed (byte* 缓冲区指针 = 缓冲流)
                {
                    fixed (byte* 临时缓冲区指针 = 缓冲流)
                    {
                        float* 缓冲区数据A = (float*)(缓冲区指针 + 流开始点);
                        float* 缓冲区数据B = (float*)临时缓冲区指针;
                        int 流终点 = 流结束点 / WaveFormat.BlockAlign * 2;
                        for (int i = 0; i < 流终点; i += 2)
                        {
                            float 音量倍率 = (float)已淡入淡出段数 / 运行状态.淡出段数;

                            if (音量倍率 >= 1)
                            {

                                音量倍率 = 1f;
                                已淡出 = true;


                            }
                            当前音量 = 音量倍率;
                            左声道A = 缓冲区数据A[i] * 音量倍率;
                            右声道A = 缓冲区数据A[i + 1] * 音量倍率;
                            左声道B = 缓冲区数据B[i] * 音量倍率;
                            右声道B = 缓冲区数据B[i + 1] * 音量倍率;


                            缓冲区数据A[i] = 左声道A;
                            缓冲区数据B[i + 1] = 右声道B;


                        }
                        已淡入淡出段数 += 1;
                    }
                }

            }


        }

        #endregion
        #region 流处理
        public void 更换流()
        {
            if (当前播放章节 + 1 > OGG读取器组.Count - 1) { 循环次数++; return; }
            OGG读取器组[当前播放章节] = OGG读取器组[当前播放章节 + 1];
            当前播放章节++;
        }
        #endregion
    }

}
